3

Regarding this problem, I updated my JHipster-Application with scripted Jenkins pipeline and have now in Jenkinsfile (partly following these hints):

[...]

 def dockerImage
    withEnv(["DOCKER_CREDS=credentials('myregistry-login')"]) {
        stage('publish docker') {
            sh "./mvnw -X -ntp jib:build"
        }
    }

with Jenkins global credentials myregistry-login saved in my Jenkins-Server to my own docker registry v2 docker-container https://myregistry.mydomain.com (domain changed for security reasons). I can successfully do a $ docker login myregistry.mydomain.com (as well as docker login https://myregistry.mydomain.com as well as docker login myregistry.mydomain.com:443) from local bash with the user and password stored in myregistry-login.

In pom.xml (following these hints as well as this, this and this):

<plugin>
  <groupId>com.google.cloud.tools</groupId>
  <artifactId>jib-maven-plugin</artifactId>
  <configuration>
    <to>
      <image>myregistry.mydomain.com:443/username/imagename</image>
      <tags>
        <tag>${maven.build.timestamp}</tag>
        <tag>latest</tag>
      </tags>
      <auth>
        <username>${env.DOCKER_CREDS_USR}</username>
        <password>${env.DOCKER_CREDS_PSW}</password>
      </auth>
    </to>
    <container>
      <jvmFlags>
        <jvmFlag>-Xms512m</jvmFlag>
        <jvmFlag>-Xmx1G</jvmFlag>
        <jvmFlag>-Xdebug</jvmFlag>
      </jvmFlags>
      <mainClass>de.myproject_name.MyApp</mainClass>
    </container>
  </configuration>
</plugin>

where username, imagename and de.myproject_name.MyApp are placeholders here.

Unfortunately I get

[DEBUG] TIMING  Retrieving registry credentials for myregistry.mydomain.com:443
[DEBUG] No credentials could be retrieved for registry myregistry.mydomain.com:443
[...]
[ERROR] I/O error for image [myregistry.mydomain.com:443/username/imagename]:
[ERROR]     Connect to myregistry.mydomain.com:443 [myregistry.mydomain.com/xxx.xxx.xxx.xxx] failed: Connection refused (Connection refused)
[DEBUG] TIMED   Authenticating push to myregistry.mydomain.com:443 : 460.0 ms
[DEBUG] TIMED   Building and pushing image : 514.0 ms
[ERROR] I/O error for image [registry-1.docker.io/library/adoptopenjdk]:
[ERROR]     Socket closed

So the withEnv isn't forwarded to Maven and/or the jib-maven-plugin is not reading the <auth>-Tag, right? What am I still doing wrong? And why is there an I/O error to registry-1.docker.io?

Jochen Haßfurter
  • 875
  • 2
  • 13
  • 27
  • Even before attempting to run `mvn`, I would first do `echo "user=$DOCKER_CREDS_USR pass=$DOCKER_CREDS_PSW"` to see if they are really defined. I don't think they are defined. And I'd also do `curl -v registry-1.docker.io/library/adoptopenjdk`. It almost seems like the Jenkins server doesn't allow the connection. – Chanseok Oh Jan 09 '20 at 15:40
  • And I'd also run the `mvn` command on my local machine to verify at least it works when the variables are properly defined. – Chanseok Oh Jan 09 '20 at 15:41
  • And I'd also try passing concrete wrong credentials, e.g., `wrongwrong` in `pom.xml` to see if the message `No credentials could be retrieved for registry` goes away. This will rule out misconfiguration in Jib. – Chanseok Oh Jan 09 '20 at 16:17
  • @ChanseokOh - if I try your suggested `echo`, I get `groovy.lang.MissingPropertyException: No such property: DOCKER_CREDS_USR for class: groovy.lang.Binding`. So how can I pass values from Jenkins global credentials to Maven correctly? I read all the links above and of course some more but no luck. Also I tried to `curl -v https://myregistry.myserver.com:443` and both, my server and the official server give the same results, which seem to be o.k - although I don't know, why registry-1.docker.io is used in second instance. – Jochen Haßfurter Jan 09 '20 at 17:28
  • @ChanseokOh - if I try `mvn` locally, I can't test the credentials from my Jenkins server. But `./mvnw -X -ntp jib:build` locally pushes the image (with given tags) successfully to my private dockerregistry. Maven then uses my `.docker/config.json`, because if I delete this file, it fails with `401 Unauthorized`. – Jochen Haßfurter Jan 09 '20 at 17:33
  • I'm pretty sure the problem is that `${env.DOCKER_CREDS_USR}` is empty or undefined. And it is empty if the environment variables are not defined where Maven runs. Testing `wronguserfoo` and checking the log will confirm this. And as for why the variable is not defined, sorry, I don't know much about Jenkins. – Chanseok Oh Jan 09 '20 at 19:23
  • Oh, and although I virtually know nothing about Jenkins, after reading one of the Jenkins docs you linked, I think it should either be `echo "user=${env.DOCKER_CREDS_USR}"` or `sh "echo $DOCKER_CREDS_USR"`. That is, if you want to directly reference an environment variable inside the Jenkins file, you should access it through `env.XXX`. OTOH, if you run an actual `echo` binary through a shell, you would directly access it with `$DOCKER_CREDS_USR`, I think. Try both approaches to see if they are defined in both cases. – Chanseok Oh Jan 09 '20 at 19:37
  • Oh, actually, my guess wasn't entirely correct. See https://stackoverflow.com/a/40718201/1701388 – Chanseok Oh Jan 09 '20 at 21:04
  • Also take a look at https://e.printstacktrace.blog/jenkins-pipeline-environment-variables-the-definitive-guide/ – Chanseok Oh Jan 09 '20 at 21:07
  • @ChanseokOh - thanks for your tipps. `sh "echo $DOCKER_CREDS_USR"` gives me `groovy.lang.MissingPropertyException` again. But `sh "echo \"user=${env.DOCKER_CREDS_USR}\"";` results in Jenkins output: `+ echo user=null user=null` - so, this is the right syntax. But the variables aren't set. https://e.printstacktrace.blog/jenkins-pipeline-environment-variables-the-definitive-guide/ uses declarative pipeline (not scripted) and in https://stackoverflow.com/a/40718201/1701388 the answer ends in the same problem (see Sebastian Woinar's comment to the answer). – Jochen Haßfurter Jan 10 '20 at 11:23
  • Cool. At least it is clear that the variables are not set at the beginning. My hunch is that `DOCKER_CREDS` is set as literal `credentials...`, not `DOCKER_CREDS_USR` and such. Try `echo ${env.DOCKER_CREDS}`. And for checking environment variables inside a shell (i.e., not Groovy variables within the Jenkinsfile script), use `sh "printenv"` to list all variables. By using `env.XXX`, you are accessing Groovy variables. (But coincidentally, inside Maven, you should also use `env.XXX` to read environment variables.) – Chanseok Oh Jan 10 '20 at 14:12
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/205758/discussion-between-chanseok-oh-and-j-gebsattel). – Chanseok Oh Jan 10 '20 at 14:59

1 Answers1

3

Finally I've got it working.

In Jenkinsfile I edit the JHipster generated code to:

    def dockerImage
    stage('publish docker') {
        withCredentials([usernamePassword(credentialsId: 'myregistry-login', passwordVariable: 'DOCKER_REGISTRY_PWD', usernameVariable: 'DOCKER_REGISTRY_USER')]) {
            sh "./mvnw -ntp jib:build"        }
    }

In pom.xml I put the jib-maven-plugin configuration:

<plugin>
  <groupId>com.google.cloud.tools</groupId>
  <artifactId>jib-maven-plugin</artifactId>
  <configuration>
    <from>
      <image>adoptopenjdk:11-jre-hotspot</image>
    </from>
    <to>
      <auth>
        <username>${DOCKER_REGISTRY_USER}</username>
         <password>${DOCKER_REGISTRY_PWD}</password>
       </auth>
       <image>myregistry.mydomain.com/myuser/my_image</image>
       <tags>
         <tag>${maven.build.timestamp}</tag>
         <tag>latest</tag>
       </tags>
     </to>
   <container>
     <jvmFlags>
       <jvmFlag>-Xms512m</jvmFlag>
       <jvmFlag>-Xmx1G</jvmFlag>
       <jvmFlag>-Xdebug</jvmFlag>
     </jvmFlags>
     <mainClass>com.mypackage.MyApp</mainClass>
     <entrypoint>
       <shell>bash</shell>
       <option>-c</option>
       <arg>chmod +x /entrypoint.sh &amp;&amp; sync &amp;&amp; /entrypoint.sh</arg>
     </entrypoint>
     <ports>
       <port>8080</port>
     </ports>
     <environment>
       <SPRING_OUTPUT_ANSI_ENABLED>ALWAYS</SPRING_OUTPUT_ANSI_ENABLED>
       <JHIPSTER_SLEEP>0</JHIPSTER_SLEEP>
     </environment>
     <creationTime>USE_CURRENT_TIMESTAMP</creationTime>
   </container>
  </configuration>
</plugin>

In my remote server setup my own docker registry v2 is running as a docker-container published via nginx-proxy with letsencrypt-nginx-proxy-companion. On the same custom network bridge runs my own jenkins server as another docker-container.

Some tests showed me that the container-name of the docker registry can not be named with the public DNS name of the registry (e.g. 'myregistry.mydomain.com' as container name). The jenkins docker-container gets the embedded docker dns server into resolv.conf, and docker will resolve the container-names of containers in the same network to the internal bridge-network IPs of these containers (only in case of custom docker networks).

I guess jib has to connect via ssl to push the docker image to the docker registry container and ssl has to be handled before the container with nginx-proxy, so the external address of the docker registry domain has to be used.

Also the docker hosts firewall has to be configured (according to this link) to allow traffic from the docker container jenkins through to the docker host. At the host it then goes back again to docker registry via nginx-proxy with ssl, right? In my case, this comes down to:

$ sudo firewall-cmd --info-zone=public
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp6s0
  sources: 
  [...] 
  rich rules: 
    rule family="ipv4" source address="172.26.0.13/32" accept
Jochen Haßfurter
  • 875
  • 2
  • 13
  • 27