Task Overview
In this tutorial, we will build an image of simple PHP Symfony application. It includes the following steps:
- Installing required software and dependencies:
php
,curl
,php-sqlite
for the application,php-xml
andphp-zip
for the composer. - Setting up an
app
user and group for the web server. - Installing the composer from a
phar
file, which is first downloaded withcurl
. - Installing other project dependencies with the composer.
- Adding the application code to the
/app
directory of the resulting image. This directory and all files in it should belong toapp:app
. - Setting up the IP address that the web server will listen to. This is done with a setting in
/apt/start.sh
, which will run when the container starts. - Making custom setup actions. As an illustration for the setup stage, we will write current date to
version.txt
.
Also, we will check that the application works and push the image in a Docker registry.
Requirements
- Minimal knowledge of Docker and Dockerfile instructions.
- Installed werf dependencies on the host system.
- Installed multiwerf on the host system.
Select werf version
This command should be run prior running any werf command in your shell session:
. $(multiwerf use 1.1 stable --as-file)
Step 1: Add a config
To implement these steps and requirements with werf we will add a special file called werf.yaml
to the application’s source code.
-
Clone the Symfony Demo Application repository to get the source code:
git clone https://github.com/symfony/symfony-demo.git cd symfony-demo
-
In the project root directory create a
werf.yaml
with the following contents:project: symfony-demo configVersion: 1 --- image: ~ from: ubuntu:16.04 docker: WORKDIR: /app # Non-root user USER: app EXPOSE: "80" ENV: LC_ALL: en_US.UTF-8 ansible: beforeInstall: - name: "Install additional packages" apt: state: present update_cache: yes pkg: - locales - ca-certificates - name: "Generate en_US.UTF-8 default locale" locale_gen: name: en_US.UTF-8 state: present - name: "Create non-root group for the main application" group: name: app state: present gid: 242 - name: "Create non-root user for the main application" user: name: app comment: "Create non-root user for the main application" uid: 242 group: app shell: /bin/bash home: /app - name: Add repository key apt_key: keyserver: keyserver.ubuntu.com id: E5267A6C - name: "Add PHP apt repository" apt_repository: repo: 'deb http://ppa.launchpad.net/ondrej/php/ubuntu xenial main' update_cache: yes - name: "Install PHP and modules" apt: name: "{{`{{packages}}`}}" state: present update_cache: yes vars: packages: - php7.2 - php7.2-sqlite3 - php7.2-xml - php7.2-zip - php7.2-mbstring - php7.2-intl - name: Install composer get_url: url: https://getcomposer.org/download/1.6.5/composer.phar dest: /usr/local/bin/composer mode: a+x install: - name: "Install app deps" shell: composer install become: yes become_user: app args: creates: /app/vendor/ chdir: /app/ setup: - name: "Create start script" copy: content: | #!/bin/bash php -S 0.0.0.0:8000 -t public/ dest: /app/start.sh owner: app group: app mode: 0755 - raw: echo `date` > /app/version.txt - raw: chown app:app /app/version.txt git: - add: / to: /app owner: app group: app
project: symfony-demo configVersion: 1 --- image: ~ from: ubuntu:16.04 docker: WORKDIR: /app # Non-root user USER: app EXPOSE: "80" ENV: LC_ALL: en_US.UTF-8 shell: beforeInstall: - apt-get update - apt-get install -y locales ca-certificates curl software-properties-common - locale-gen en_US.UTF-8 - groupadd -g 242 app - useradd -m -d /app -g 242 -u 242 -s /bin/bash app # https://askubuntu.com/posts/490910/revisions - LC_ALL=C.UTF-8 add-apt-repository -y ppa:ondrej/php - apt-get update - apt-get install -y php7.2 php7.2-sqlite3 php7.2-xml php7.2-zip php7.2-mbstring php7.2-intl - curl -LsS https://getcomposer.org/download/1.4.1/composer.phar -o /usr/local/bin/composer - chmod a+x /usr/local/bin/composer install: - cd /app # NOTICE: Always use `composer install` command in real world environment! - su -c 'composer update' app setup: - "echo '#!/bin/bash' >> /app/start.sh" - echo 'php -S 0.0.0.0:8000 -t public/' >> /app/start.sh - echo `date` > /app/version.txt - chown app:app /app/start.sh /app/version.txt - chmod +x /app/start.sh git: - add: / to: /app owner: app group: app
Step 2: Build and Run the Application
Let’s build and run our first application.
-
cd
to the project root directory. -
Build an image:
werf build --stages-storage :local
There is a known issue in composer, so if you’ve got the
proc_open(): fork failed - Cannot allocate memory
error when running build add 1GB swap file. How to add swap space read here. -
Run a container from the image:
werf --stages-storage :local run --docker-options="-d -p 8000:8000" -- /app/start.sh
-
Check that the application runs and responds:
curl localhost:8000
Step 3: Push image into Docker registry
werf can be used to push a built image into Docker registry.
-
Run local Docker registry:
docker run -d -p 5000:5000 --restart=always --name registry registry:2
-
Publish image with werf using custom tagging strategy with docker tag
v0.1.0
:werf publish --stages-storage :local --images-repo localhost:5000/symfony-demo --tag-custom v0.1.0
What Can Be Improved
This example has space for further improvement:
- Set of commands for creating
start.sh
can be easily replaced with a single git command, and the file itself stored in the git repository. - As we copy files with a git command, we can set file permissions with the same command.
composer install
instead ofcomposer update
should be used to install dependencies with versions fixed in filescomposer.lock
,package.json
andyarn.lock
. Also, it’s best to first check these files and runcomposer install
when needed. To solve this problem werf have so-calledstageDependencies
directive.
These issues are further discussed in reference.